package vip.xumy.core.utils;

import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;

import lombok.extern.log4j.Log4j2;

/**	All rights reserved
 *	author:mengyxu
 *	date:2020年6月10日
 */

@Log4j2
public class RSAUtil {  
    /** 
     * 定义加密方式 
     */  
    private final static String KEY_RSA = "RSA";  
    /** 
     * 定义签名算法 
     */  
    private final static String KEY_RSA_SIGNATURE = "MD5withRSA";  
    
    /** 
     * 定义公钥算法 
     */  
    private final static String KEY_RSA_PUBLICKEY = "RSAPublicKey";  
    
    /** 
     * 定义私钥算法 
     */  
    private final static String KEY_RSA_PRIVATEKEY = "RSAPrivateKey";  
  
    /** 
     * 用私钥对信息生成数字签名 
     * @param data 加密数据 
     * @param privateKey 私钥 
     * @return 
     */  
    public static String sign(byte[] data, String privateKey) {  
        String str = "";  
        try {  
            //转码16进制字符串
            byte[] bytes = StringToByteKey(privateKey);  
            // 构造PKCS8EncodedKeySpec对象  
            X509EncodedKeySpec pkcs = new X509EncodedKeySpec(bytes);  
            // 指定的加密算法  
            KeyFactory factory = KeyFactory.getInstance(KEY_RSA);  
            // 取私钥对象  
            PrivateKey key = factory.generatePrivate(pkcs);  
            // 用私钥对信息生成数字签名  
            Signature signature = Signature.getInstance(KEY_RSA_SIGNATURE);  
            signature.initSign(key);  
            signature.update(data);  
            str = byteToStringKey(signature.sign());  
        } catch (Exception e) {  
            log.error("Sign failed", e);
        }  
        return str;  
    }  
  
    /** 
     * 校验数字签名 
     * @param data 加密数据 
     * @param publicKey 公钥 
     * @param sign 数字签名 
     * @return 校验成功返回true，失败返回false 
     */  
    public static boolean verify(byte[] data, String publicKey, String sign) {  
        boolean flag = false;  
        try {  
            // 解密由base64编码的公钥  
            byte[] bytes = StringToByteKey(publicKey);  
            // 构造X509EncodedKeySpec对象  
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);  
            // 指定的加密算法  
            KeyFactory factory = KeyFactory.getInstance(KEY_RSA);  
            // 取公钥对象  
            PublicKey key = factory.generatePublic(keySpec);  
            // 用公钥验证数字签名  
            Signature signature = Signature.getInstance(KEY_RSA_SIGNATURE);  
            signature.initVerify(key);  
            signature.update(data);  
            flag = signature.verify(StringToByteKey(sign));  
        } catch (Exception e) {  
            log.error("Verify failed", e);
        }  
        return flag;  
    }  
  
    /** 
     * 公钥解密 
     * @param data 加密数据 
     * @param key 公钥 
     * @return 
     */  
    public static String decryptByPublicKey(String data, String key) {  
        try {
        	//解码公钥
        	byte[] pubKey = Base64Util.decode(key);
            // 取得公钥  
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKey);  
            KeyFactory factory = KeyFactory.getInstance(KEY_RSA);  
            PublicKey publicKey = factory.generatePublic(keySpec);  
            // 对数据解密  
            Cipher cipher = Cipher.getInstance(factory.getAlgorithm());  
            cipher.init(Cipher.DECRYPT_MODE, publicKey);  
            byte[] result = cipher.doFinal(Base64Util.decode(data)); 
            if(result != null) {
            	return  new String(result);
            } 
        } catch (Exception e) {  
            log.error("decryptByPublicKey fail-",e);
        }  
        return null;
    }  
  
    /** 
     * 公钥加密 
     * @param data 待加密数据 
     * @param key 公钥 
     * @return 
     */  
    public static String encryptByPublicKey(String data, String key) {  
        try {
        	byte[] pubKey = Base64Util.decode(key);
            // 取得公钥  
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKey);  
            KeyFactory factory = KeyFactory.getInstance(KEY_RSA);  
            PublicKey publicKey = factory.generatePublic(keySpec);  
            // 对数据加密  
            Cipher cipher = Cipher.getInstance(factory.getAlgorithm());  
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
            byte[] result = cipher.doFinal(data.getBytes());
            if(result != null) {
            	return  Base64Util.encode(result);
            } 
        } catch (Exception e) {  
            log.error("encryptByPublicKey failed-",e);
        }  
        return null;
    }
    
    /** 
     * 私钥解密 
     * @param data 加密数据 
     * @param key 私钥 
     * @return 
     */  
    public static byte[] decryptByPrivateKey(byte[] data, String key) {  
        byte[] result = null;  
        try {  
            // 对私钥解密  
            byte[] bytes = StringToByteKey(key);  
            // 取得私钥  
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);  
            KeyFactory factory = KeyFactory.getInstance(KEY_RSA);  
            PrivateKey privateKey = factory.generatePrivate(keySpec);  
            // 对数据解密  
            Cipher cipher = Cipher.getInstance(factory.getAlgorithm());  
            cipher.init(Cipher.DECRYPT_MODE, privateKey);  
            result = cipher.doFinal(data);  
        } catch (Exception e) {  
            log.error("decryptByPrivateKey failed", e);
        }  
        return result;  
    }  
  
    /** 
     * 私钥加密 
     * @param data 待加密数据 
     * @param key 私钥 
     * @return 
     */  
    public static byte[] encryptByPrivateKey(byte[] data, String key) {  
        byte[] result = null;  
        try {  
            byte[] bytes = StringToByteKey(key);  
            // 取得私钥  
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);  
            KeyFactory factory = KeyFactory.getInstance(KEY_RSA);  
            PrivateKey privateKey = factory.generatePrivate(keySpec);  
            // 对数据加密  
            Cipher cipher = Cipher.getInstance(factory.getAlgorithm());  
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);  
            result = cipher.doFinal(data);  
        } catch (Exception e) {  
            log.error("encryptByPrivateKey failed", e);
        }  
        return result;  
    }
    
    /** 
     * 初始化密钥 
     * @return 
     */  
    public static Map<String, Object> init() {  
        Map<String, Object> map = null;  
        try {  
            KeyPairGenerator generator = KeyPairGenerator.getInstance(KEY_RSA);  
            generator.initialize(1024);  
            KeyPair keyPair = generator.generateKeyPair();  
            // 公钥  
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();  
            // 私钥  
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();  
            // 将密钥封装为map  
            map = new HashMap<>();  
            map.put(KEY_RSA_PUBLICKEY, publicKey);  
            map.put(KEY_RSA_PRIVATEKEY, privateKey);  
        } catch (NoSuchAlgorithmException e) {  
            log.error("Exception occurred!", e);  
        }  
        return map;  
    }  

    /** 
     * 获取公钥 
     * @param map 
     * @return 
     */  
    public static String getPublicKey(Map<String, Object> map) {  
        String str = "";  
        try {  
            Key key = (Key) map.get(KEY_RSA_PUBLICKEY);  
            str = byteToStringKey(key.getEncoded());  
        } catch (Exception e) {  
            log.error("getPublicKey failed", e);
        }  
        return str;  
    }  
  
    /** 
     * 获取私钥 
     * @param map 
     * @return 
     */  
    public static String getPrivateKey(Map<String, Object> map) {  
        String str = "";  
        try {  
            Key key = (Key) map.get(KEY_RSA_PRIVATEKEY);  
            str = byteToStringKey(key.getEncoded());  
        } catch (Exception e) {  
            log.error("getPrivateKey failed", e);
        }  
        return str;  
    }
    
    public static String byteToStringKey(byte[] key) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < key.length; i++) {
			if ((key[i] & 0xff) < 0x10) {
				sb.append("0");
			}
			sb.append(Integer.toHexString(0xFF & key[i]));
		}
		return sb.toString().toUpperCase();
	}
	
	public static byte[] StringToByteKey(String key) {
		key = key.toUpperCase();
		byte[] arr = new byte[key.length() / 2];
		int k = 0;
		for (int i = 0; i < arr.length; i++) {//因为是16进制，最多只会占用4位，转换成字节需要两个16进制的字符，高位在先
			byte high = (byte) (Character.digit(key.charAt(k), 16) & 0xff);
			byte low = (byte) (Character.digit(key.charAt(k + 1), 16) & 0xff);
			arr[i] = (byte) (high << 4 | low);
			k += 2;
		}
		return arr;
	}
  
    private RSAUtil(){
    	//Do nothing.
    }
}